<?php


namespace models\Business;


use models\DAO\BookingModel;
use models\Exception\Business\AlibabacategoryException;
use models\Exception\Business\AlibabaproductException;
use models\Exception\Business\BookingException;
use models\Exception\DAO\ModelDriverException;
use models\Exception\DAO\ModelException;
use models\Exception\DAO\ModelReflectionException;
use models\Exception\DAO\ModelSqlException;
use models\Exception\Service\ServiceException;
use models\Exception\Transformer\TransformerException;
use models\Service\SystemService;
use Yaf\Registry;
use youliPhpLib\Common\StringOperation;

/**
 * 拼团类
 * Class Coupon
 * @package models\Business
 */
class Coupon
{
    // redis客户端
    private $redis = null;
    // 商品编号
    private $product_id = '';
    // 商品数量
    private $num = 0;
    private $num_arr = [];
    // 商品sku id
    private $sku_id = '';
    private $sku_arr = [];
    // 购买会员编号
    private $member_id = '';
    // 指定商品的库存数
    private $local_product_store_key = '';
    // 指定sku_id的库存数
    private $local_product_sku_store_key = '';
    // 拼团未支付用户队列
    private $local_unpaid_coupon_member_list_key = '';
    // 拼团已支付用户队列
    private $local_paid_coupon_member_list_key = '';

    // 1688接口返回的商品信息
    private $product_info_from_remote = [];
    // 1688接口返回的商品sku信息
    private $sku_from_remote = [];
    // 本地数据库中的商品信息
    private $product_from_local = [];

    // 是否需要修改数据库中指定商品的状态(上线 -> 下线)
    private $product_off_shelf = false;
    // 是否需要修改数据库中指定商品的库存
    private $product_sold_out = false;
    // 是否需要修改数据库中指定商品、指定sku的库存
    private $product_sku_sold_out = false;
    // 是否需要修改商品的最小起订量
    private $update_product_min_order_quantity = false;

    // 数据库
    private $db = null;

    private $booking_data = [];

    /**
     * @return array
     */
    public function getProductFromLocal() {
        return $this->product_from_local;
    }

    public function __construct($product_id, $num, $member_id, $sku_id = '')
    {
        $this->redis = Registry::get('redis');

        try {
            $this->redis->ping();
        } catch (\Exception $e) {
            $this->redis->auth(Registry::get('config')['redis']['master']['auth']);
        }

        // 检测本地库存是否有效
        $this->redis->select(Registry::get('config')['redis']['product_store']['db']);

        $this->product_id = $product_id;
        $this->num = $num;
        array_push($this->num_arr, $num);
        $this->member_id = $member_id;
        $this->sku_id = $sku_id;
        if (!empty($sku_id))
            array_push($this->sku_arr, $sku_id);

        $this->local_product_store_key = "product:store:" . $product_id;
        if (!empty($sku_id))
            $this->local_product_sku_store_key = "product:store:" . $product_id . ':' . $sku_id;
        $this->local_unpaid_coupon_member_list_key = 'product:wait4pay:' . $this->product_id;
        $this->local_paid_coupon_member_list_key = 'product:buyer_list:' . $this->product_id;

        $model = BookingModel::getInstance();
        $this->db = $model->getConn();
    }

    /**
     * 校验本地库存
     * @throws AlibabaproductException
     */
    public function checkStore() {
        // sku存在
        if (! empty($this->local_product_sku_store_key)) {
            // 判断指定商品的sku库存是否不存在 或数量小于0
            if (! $this->redis->exists($this->local_product_sku_store_key) || $this->redis->get($this->local_product_sku_store_key) <= 0) {
                Registry::get('order_log')->err("校验商品本地库存时报错,商品编号:" . $this->product_id . ", 商品sku: " . $this->sku_id . " , 原因: " . AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE . ", line: " . __LINE__);
                $product_info = $this->getProductFromLocal();
                if (isset($product_info['title']))
                    $msg = str_replace('%title', $product_info['title'], AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE_4_API);
                else
                    $msg = AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE;
                throw new AlibabaproductException($msg, AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE_NO);
            }
        }
        // sku不存在
        else {
            // 判断商品库存是否不存在 或数量小于0
            if (! $this->redis->exists($this->local_product_store_key) || $this->redis->get($this->local_product_store_key) <= 0) {
                Registry::get('order_log')->err("校验商品本地库存时报错,商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE . ", line: " . __LINE__);
                $product_info = $this->getProductFromLocal();
                if (isset($product_info['title']))
                    $msg = str_replace('%title', $product_info['title'], AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE_4_API);
                else
                    $msg = AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE;
                throw new AlibabaproductException($msg, AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE_NO);
            }
        }
    }

    public function delStore() {

    }

    /**
     * 设置库存
     */
    public function setStore() {
        if (!empty($this->sku_id)) {
            Registry::get('order_log')->info("修改库存" . $this->local_product_sku_store_key . '库存：' . $this->num);
            $this->redis->watch($this->local_product_sku_store_key);
            $this->redis->set($this->local_product_sku_store_key, $this->num);
        } else {
            Registry::get('order_log')->info("修改库存" . $this->local_product_store_key . '库存：' . $this->num);
            $this->redis->watch($this->local_product_store_key);
            $this->redis->set($this->local_product_store_key, $this->num);
        }
        $res = $this->redis->exec();
        Registry::get('order_log')->info("res" . json_encode($res));

        if (!empty($this->local_product_sku_store_key))
            $this->redis->unwatch($this->local_product_sku_store_key);
        if (!empty($this->local_product_store_key))
            $this->redis->unwatch($this->local_product_store_key);
    }

    /**
     * 重置库存
     */
    public function resetStore() {
        try {
            // 删除缓存
            $this->delStore();
        } catch (\Exception $e) {
            // 缓存不存在
        } finally {
            // 生成缓存
            $this->setStore();
        }
    }

    /**
     * 校验当前用户是否允许发起拼团
     * @throws AlibabaproductException
     */
    public function memberCouponIsValid() {
        // 校验当前用户是否有尚未结束的，当前商品的拼团，若有则提示用户"请等待上一次拼团结束再参与"
        $unpaid_member_arr = $this->redis->zRange($this->local_unpaid_coupon_member_list_key, 0, -1, true);
        if (in_array($this->member_id, array_values($unpaid_member_arr)) === true) {
            Registry::get('order_log')->err("校验商品本地库存时报错,商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_TRADE_UNPAY . ", line: " . __LINE__);
            throw new AlibabaproductException(AlibabaproductException::ALIBABAPRODUCT_TRADE_UNPAY, AlibabaproductException::ALIBABAPRODUCT_TRADE_UNPAY_NO);
        }
        $paid_member_arr = $this->redis->lRange('product:paid:'.$this->product_id, 0, -1);
        $member_arr=[];
        foreach ($paid_member_arr as $v){
            $paid_member=json_decode($v,true);
            if(isset($paid_member['member_id'])){
                $member_arr[]=$paid_member['member_id'];
            }
        }
        if (in_array($this->member_id, array_values($member_arr)) === true) {
            Registry::get('order_log')->err("校验商品本地库存时报错,商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_TRADE_PAY_UNFINISHED . ", line: " . __LINE__);
            throw new AlibabaproductException(AlibabaproductException::ALIBABAPRODUCT_TRADE_PAY_UNFINISHED, AlibabaproductException::ALIBABAPRODUCT_TRADE_PAY_UNFINISHED_NO);
        }
    }

    /**
     * 调用1688接口 校验1688商品有效性与库存有效性
     * @param int $total_num
     * @return bool|string|string[]
     */
    public function checkStoreFromAlibaba($total_num = 0) {
        // 商品存在调用接口获取数据
        list($product_arr, $sku_arr, $product_sku_arr, $shipping_arr, $freight_arr) =
            Alibaba::getProductCpsMediaProductInfo($this->product_id);
        Registry::get('order_log')->info(__METHOD__ . ' response: ' .
            json_encode([$product_arr, $sku_arr, $product_sku_arr, $shipping_arr, $freight_arr]));
        $sku_id_arr = array_column($product_sku_arr,'amount_on_sale', 'sku_id');

        // alibaba接口没有返回指定product_id的商品信息，表示商品不存在 抛错
        if (empty($product_arr)) {
            $this->product_off_shelf = true;
            Registry::get('order_log')->err("校验远程库存时报错, 商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_NOT_EXISTS . ", line: " . __LINE__);
            return false;
        }

        $this->product_info_from_remote = $product_arr;
        $this->sku_from_remote = $sku_id_arr;

        $num = $total_num === 0 ? $this->num : $total_num;
        // alibaba接口返回的min_order_quantity 大于 购买数量时 提示用户购买数量小于最小起订量
        /*if ($product_arr['min_order_quantity'] > $num) {
            $this->update_product_min_order_quantity = true;
            $msg = str_replace('#num#', $product_arr['min_order_quantity'], AlibabaproductException::ALIBABAPRODUCT_INVALID_NUM);
            Registry::get('order_log')->err("商品起订量出错, 商品编号:" . $this->product_id . ", 原因: " . $msg . ", line: " . __LINE__);
            return $msg;
        }*/

        // alibaba接口返回的amount_on_sale 小于 购买数量时 提示用户商品库存不足
        if ($product_arr['amount_on_sale'] < $num) {
            $this->product_off_shelf = $product_arr['amount_on_sale'] == 0;
            $this->product_sold_out = true;
            Registry::get('order_log')->err("校验远程库存时报错, 商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE . ", line: " . __LINE__);
        }

        // 指定了sku && alibaba接口返回了指定sku_id的库存数量 && alibaba指定sku_id的库存数量小于购买数量时 提示用户商品库存不足
        if (!empty($this->sku_id) && isset($sku_id_arr[$this->sku_id]) && $sku_id_arr[$this->sku_id] < $num) {
            $this->product_sku_sold_out = true;
            Registry::get('order_log')->err("校验远程库存时报错, 商品编号:" . $this->product_id . ", 原因: " . AlibabaproductException::ALIBABAPRODUCT_INVALID_STORE . ", line: " . __LINE__);
        }

        return true;
    }

    /**
     * 更新alibabaproduct表
     */
    public function updateAlibabaProduct() {
        // 需要修改数据库中 指定商品的可销售数量
        if ($this->product_sold_out === true) {
            $sql = "UPDATE `alibabaproduct` SET `amount_on_sale` = '" . $this->product_info_from_remote['amount_on_sql'] . "' WHERE `product_id` = '" . $this->product_id . "'";
            Registry::get('order_log')->info("商品编号为" . $this->product_id . "的商品卖完了，需要修改数据库中的商品库存");
            Registry::get('order_log')->info("sql: " . $sql);
            $this->db->executeQuery($sql);
        }

        // 需要修改数据库中 指定商品的最小起订量
        if ($this->update_product_min_order_quantity === true) {
            Registry::get('order_log')->info("商品编号为" . $this->product_id . "需要修改最小起订量为" . $this->product_info_from_remote['min_order_quantity']);
            $sql = "UPDATE `alibabaproduct` SET `min_order_quantity` = '" . $this->product_info_from_remote['min_order_quantity'] . "' WHERE `product_id` = '" . $this->product_id . "'";
            Registry::get('order_log')->info("sql: " . $sql);
            $this->db->executeQuery($sql);
        }
    }

    /**
     * 下架指定商品
     */
    public function invalidAlibabaProduct() {
        // 需要修改数据库中 指定商品的状态
        if ($this->product_off_shelf === true) {
            Registry::get('order_log')->info("商品编号为" . $this->product_id . "的商品需要修改数据库中的商品状态");
            $sql = "UPDATE `alibabaproduct` SET `stats` = '0' WHERE `product_id` = '" . $this->product_id . "'";
            Registry::get('order_log')->info("sql: " . $sql);
            $this->db->executeQuery($sql);
        }
    }

    /**
     * 更新alibabasku表
     */
    public function updateAlibabaProductSku() {
        // 需要修改数据库中 指定商品的指定sku的可销售数量
        if ($this->product_sku_sold_out === true) {
            Registry::get('order_log')->info("商品编号为" . $this->product_id . "，sku编号为" . $this->product_sku_sold_out . "商品卖完了，需要修改数据库中的sku库存");
            $sql = "UPDATE `alibabasku` SET `amount_on_sale` = '" . $this->sku_from_remote['amount_on_sale'] . "' WHERE `sku_id` = '" . $this->sku_id . "'";
            Registry::get('order_log')->info("sql: " . $sql);
            $this->db->executeQuery($sql);
        }
    }

    /**
     * 更新redis库存
     * @throws AlibabaproductException
     */
    public function decrStoreByProductId() {
        // 扣除本地库存
        try {
            if (!empty($this->local_product_sku_store_key)) {
                $key = $this->local_product_sku_store_key;
                $sql = "UPDATE `alibabasku` SET `amount_on_sale` = `amount_on_sale` - " . $this->num . " WHERE `sku_id` = '" . $this->sku_id . "'";
                Registry::get('order_log')->info("商品编号为" . $this->product_id . "，sku编号为" . $this->sku_id . "修改数据中的库存数量。sql: " . $sql);
                $this->db->executeQuery($sql);
            } else {
                $key = $this->local_product_store_key;
                $sql = "UPDATE `alibabaproduct` SET `amount_on_sale` = `amount_on_sale` - " . $this->num . " WHERE `product_id` = '" . $this->product_id . "'";
                Registry::get('order_log')->info("商品编号为" . $this->product_id . "修改数据中的库存数量。sql: " . $sql);
                $this->db->executeQuery($sql);
            }

            $this->redis->watch($key);
            $this->redis->decrBy($key, $this->num);
            $this->redis->exec();
        } catch (\Exception $e) {
            $msg = $e->getMessage();
            Registry::get('order_log')->err("修改本地商品库存时失败, 原因: " . $msg);
        } finally {
            $this->redis->unwatch($key);

            if (! empty($msg))
                throw new AlibabaproductException(AlibabaproductException::ALIBABAPRODUCT_COUPON_FAILURE, AlibabaproductException::ALIBABAPRODUCT_COUPON_FAILURE_NO);
        }
    }

    /**
     * 读取数据库中的指定商品信息
     * @throws AlibabacategoryException
     * @throws AlibabaproductException
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws ServiceException
     * @throws TransformerException
     */
    public function getProductInfoFromLocal() {
        // 校验用户所支付的金额
        try {
            $product = AlibabaProduct::getProduct(['id' => $this->product_id, 'from_api' => true]);
            if ($product['data'][0]['stats'] != 1)
                throw new AlibabaproductException(AlibabaproductException::ALIBABAPRODUCT_NOT_EXISTS, AlibabaproductException::ALIBABAPRODUCT_NOT_EXISTS_NO);

            $this->product_from_local = $product['data'][0];

            // 计算价格
            // 直接读取redis中的platform_price作为价格
            $this->product_from_local['amount'] = $this->product_from_local['platform_price'];
            // 商品存在"超买价"，最终售价 = 超买价
            /*if (isset($this->product_from_local['promotion_price']) && $this->product_from_local['promotion_price'] != '0.00') {
                $this->product_from_local['amount'] = $this->product_from_local['promotion_price'];
            }
            // 商品存在"渠道专享价"，最终售价 = 渠道专享价
            else if (isset($this->product_from_local['channel_price']) && $this->product_from_local['channel_price'] != '0.00') {
                $this->product_from_local['amount'] = $this->product_from_local['channel_price'];
            }
            // 商品存在"sku建议零售价"，最终售价 = 最高sku建议零售价
            // 商品存在"sku分销基准价"，最终售价 = 最高sku分销基准价
            else if (!empty($this->sku_id) && isset($this->product_from_local['sku']) && is_array($this->product_from_local['sku'])) {
                foreach ($this->product_from_local['sku'] as $idx => $sku_info) {
                    if ($this->sku_id == $sku_info['sku_id']) {
                        if ($sku_info['retail_price'] !== '0.00') {
                            $this->product_from_local['amount'] = $sku_info['retail_price'];
                        } else if ($sku_info['consign_price'] !== '0.00') {
                            $this->product_from_local['amount'] = $sku_info['consign_price'];
                        }
                    }
                }
            }
            // 商品存在"CPS建议价"，最终售价 = price_rate / 100 * CPS建议价
            else if ($this->product_from_local['cps_suggest_price'] !== '0.00') {
                $this->product_from_local['amount'] = $this->product_from_local['cps_suggest_price'];
            }
            else {
                $this->product_from_local['amount'] = $this->product_from_local['consign_price'];
            }*/

        } catch (AlibabacategoryException $e) {
            throw $e;
        } catch (AlibabaproductException $e) {
            throw $e;
        } catch (ModelDriverException $e) {
            throw $e;
        } catch (ModelException $e) {
            throw $e;
        } catch (ModelReflectionException $e) {
            throw $e;
        } catch (ModelSqlException $e) {
            throw $e;
        } catch (ServiceException $e) {
            throw $e;
        } catch (TransformerException $e) {
            throw $e;
        }
    }

    public function getIntegralOrderData($sku_id = 0, $num = 0) {
        try {
            $this->getProductInfoFromLocal();

            $price_arr = $amount_arr = $integral_arr = [];
            if (!empty($this->sku_arr)) {
                foreach ($this->product_from_local['sku'] as $idx => $sku_info) {
                    if (false !== $key = array_search($sku_info['sku_id'], $this->sku_arr)) {
                        array_push($price_arr, $sku_info['platform_price'] * $this->num_arr[$key]);
                        array_push($integral_arr, $sku_info['integral'] * $this->num_arr[$key]);
                        $amount_arr[$sku_info['sku_id']] = $sku_info['platform_price'];
                    }
                }
            } else {

                array_push($price_arr, $this->product_from_local['platform_price'] * $this->num);
                array_push($integral_arr, $this->product_from_local['integral'] * $this->num);
                if ($this->sku_id)
                    $amount_arr[$this->sku_id] = $price_arr[0];
                else
                    $amount_arr = $price_arr[0];
            }

            if (empty($price_arr) || empty($integral_arr))
                throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);
            $total_integral = (int)sprintf('%.2f', array_sum($integral_arr));
            $total_amount = sprintf('%.2f', array_sum($price_arr));
            if ($total_integral == 0 || $total_amount == 0)
                throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);

            return [
                // 付款信息订单号
                'sn' => StringOperation::genOrderSn(),
                // 付款会员编号 Registry::get('jwt_info')->id获取
                'member_id' => $this->member_id,
                // 商品编号
                'product_id' => $this->product_id,
                'sku_id' => ! empty($sku_id) ? $sku_id : $this->sku_id,
                // 商品数量 可调整
                'num' => ! empty($num) ? $num : $this->num,
                // 付款金额
//                'amount' => $this->product_from_local['amount'] * (! empty($num) ? $num : $this->num),
                'integral' => $total_integral,
                'amounts' => $total_amount,
                'amount' => $amount_arr,
                // 时间
                'created_at' => date('Y-m-d H:i:s', time()),
                // 过期时间
                'expired_at' => '0000-00-00 00:00:00',
                // 收货地址编号
//                'address_id' => 0,
                // 收款通道 0为支付宝 1为微信
//                'channel' => 0,
                // 订购信息状态 未支付
//                'stats' => 0
            ];

        } catch (\Exception $e) {
            exit($e->getMessage());
        }
    }

    /**
     * 获取订单信息数据
     * @param int $sku_id
     * @param int $num
     * @param string $province_name
     * @return array
     * @throws AlibabaproductException
     */
    public function getBookingData($sku_id = 0, $num = 0, $province_name = '', $address_id = 0) {
        try {
            $this->getProductInfoFromLocal();

            $shipping = AlibabaProduct::getShippingFeeByProductIdProvince(
                $this->product_id, $province_name, isset($parameters['num']) && !empty($parameters['num']) ? $parameters['num'] : 1);
            $fee = !empty($shipping) && isset($shipping['real_fee']) ? $shipping['real_fee'] : 0.00;

            // 此处暂时先设置为固定值，正确情况为：system表中读取"拼团时长"字段的值
            $system_deadline_time = 20;

            $price_arr = [];
            if (!empty($this->sku_arr)) {
                foreach ($this->product_from_local['sku'] as $idx => $sku_info) {
                    if (false !== $key = array_search($sku_info['sku_id'], $this->sku_arr)) {
                        array_push($price_arr, $sku_info['platform_price'] * $this->num_arr[$key]);
//                        array_push($price_arr, [$key, $sku_info['sku_id'], $this->num_arr[$key], $sku_info['platform_price']]);
                    }
                }
            } else
                array_push($price_arr, $this->product_from_local['amount'] * $this->num);

            if (empty($price_arr))
                throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);
            $total_amount = sprintf('%.2f', array_sum($price_arr));
            if ($total_amount == 0)
                throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);

            $total_amount += $fee;

            $this->booking_data = [
                // 付款信息订单号
                'sn' => StringOperation::genOrderSn(),
                // 付款会员编号 Registry::get('jwt_info')->id获取
                'member_id' => $this->member_id,
                // 商品编号
                'product_id' => $this->product_id,
                'sku_id' => ! empty($sku_id) ? $sku_id : implode(',', $this->sku_arr),
                // 商品数量 可调整
                'num' => ! empty($num) ? $num : implode(',', $this->num_arr),
                // 付款金额
//                'amount' => $this->product_from_local['amount'] * (! empty($num) ? $num : $this->num),
                'amount' => $total_amount,
                // 向用户收取运费
                'fee' => $fee,
                // 时间
                'created_at' => date('Y-m-d H:i:s', time()),
                // 过期时间
                'expired_at' => date('Y-m-d H:i:s', time() + ($system_deadline_time * 60)),
                // 收货地址编号
                'address_id' => $address_id,
                // 收款通道 0为支付宝 1为微信
//                'channel' => 0,
                // 订购信息状态 未支付
//                'stats' => 0
            ];

            // 商品sku编号 可能不存在
            if (!empty($this->sku_id))
                $this->booking_data['sku_id'] = ! empty($sku_id) ? $sku_id : implode(',', $this->sku_arr);

            $this->redis->zAdd('product:wait4pay:' . $this->product_id, [], $this->member_id, json_encode($this->booking_data));

            return $this->booking_data;

        } catch (\Exception $e) {
            Registry::get('order_log')->err($e->getMessage());
            throw new AlibabaproductException(AlibabaproductException::ALIBABAPRODUCT_NOT_EXISTS, AlibabaproductException::ALIBABAPRODUCT_NOT_EXISTS_NO);
        }

    }

    /**
     * 根据booking的sn，从wait4pay队列中读取指定数据，写入到paid队列
     * @param $sn
     * @return array
     * @throws BookingException
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws ServiceException
     * @throws TransformerException
     */
    public function setPaidList($sn) {
        $unpaid_list = $this->redis->zRange($this->local_unpaid_coupon_member_list_key, 0, -1, true);
        $member_id_arr = array_values($unpaid_list);
        $booking_data_arr = array_keys($unpaid_list);

        // 订购信息不存在于redis中
        if (empty($booking_data_arr))
            throw new BookingException(BookingException::BOOKING_NOT_EXISTS, BookingException::BOOKING_NOT_EXISTS_NO);

        // 会员编号不匹配
        if (false === $key = array_search($this->member_id, $member_id_arr))
            throw new BookingException(BookingException::INVALID_MEMBER, BookingException::INVALID_MEMBER_NO);

        $this->booking_data = $booking_data_arr[$key];
        $booking_data = json_decode($booking_data_arr[$key], true);
        // sn不匹配
        if ($sn !== $booking_data['sn'])
            throw new BookingException(BookingException::BOOKING_NOT_EXISTS, BookingException::BOOKING_NOT_EXISTS_NO);

        $refund = false;
        $system_deadline_time = 0;
        // 收到异步回调时已经超时 退回余额
        if (strtotime($booking_data['expired_at']) <= time())
            $refund = true;
        // 收到异步回调时尚未超时 生成"product:paid:拼团结束时间的时间戳"队列，用以ticker从该队列中获取对应数据进行拼团
        else {
            $booking_data = json_decode($this->booking_data, true);
            $system_info = SystemService::getInstance()->getOne(1);
            $system_deadline_time = $system_info['coupon_expire'];
            $len = $this->redis->lpush('product:paid:' . $booking_data['product_id'] /*. ':' . $booking_data['num']*/, json_encode([
                'member_id' => $booking_data['member_id'],
                'num' => $booking_data['num'],
                'product_id' => $booking_data['product_id'],
                'sn' => $booking_data['sn']
            ]));
            Registry::get('order_log')->info("当前" . $booking_data['product_id'] . "-" . $booking_data['num'] . "的待拼团队列中有名" . $len . "等待者");
        }

        // 删除"product:wait4pay:product_id"队列中的指定数据
        $this->redis->zRem($this->local_unpaid_coupon_member_list_key, $this->booking_data);

        return [$refund, $system_deadline_time];
    }

    /**
     * @param string $sn 订购信息订单号
     * @param string $address_id 收货地址编号
     * @param integer $channel 支付渠道 0为支付宝 1为微信
     * @param $sn
     * @param $address_id
     * @param $channel
     * @return mixed
     * @throws AlibabacategoryException
     * @throws AlibabaproductException
     * @throws BookingException
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws ServiceException
     * @throws TransformerException
     */
    public function getBookingDataFromRedis($sn, $address_id, $channel) {
        try {
            $unpaid_list = $this->redis->zRange($this->local_unpaid_coupon_member_list_key, 0, -1, true);
            $member_id_arr = array_values($unpaid_list);
            $booking_data_arr = array_keys($unpaid_list);

            // 订购信息不存在于redis中
            if (empty($booking_data_arr))
                throw new BookingException(BookingException::BOOKING_NOT_EXISTS, BookingException::BOOKING_NOT_EXISTS_NO);

            // 会员编号不匹配
            if (false === $key = array_search($this->member_id, $member_id_arr))
                throw new BookingException(BookingException::INVALID_MEMBER, BookingException::INVALID_MEMBER_NO);

            $this->booking_data = $booking_data_arr[$key];
            $booking_data = json_decode($booking_data_arr[$key], true);

            // sn不匹配
            if ($sn !== $booking_data['sn'])
                throw new BookingException(BookingException::BOOKING_NOT_EXISTS, BookingException::BOOKING_NOT_EXISTS_NO);

            // 超时
            if (strtotime($booking_data['expired_at']) <= time()) {
                $this->redis->zRem($this->local_unpaid_coupon_member_list_key, $this->booking_data);
                // 删除队列
                throw new BookingException(BookingException::BOOKING_DATA_EXPIRED, BookingException::BOOKING_DATA_EXPIRED_NO);
            }
            $data_arr = json_decode($this->booking_data, true);

            // 还原商品真实价格
            $fee = $data_arr['fee'];
            $data_arr['amount'] = $data_arr['amount'] - $data_arr['fee'];

            $update_data = $update_shipping_fee = false;
            // 商品数量发生了变化
            if ($this->num_arr !== explode(',', $data_arr['num'])) {
//            if (array_diff($this->num_arr, explode(',', $data_arr['num']))) {
                $this->getProductInfoFromLocal();

                $price_arr = [];
                if (!empty($this->sku_arr)) {
                    foreach ($this->product_from_local['sku'] as $idx => $sku_info) {
                        if (false !== $key = array_search($sku_info['sku_id'], $this->sku_arr)) {
                            array_push($price_arr, $sku_info['platform_price'] * $this->num_arr[$key]);
//                        array_push($price_arr, [$key, $sku_info['sku_id'], $this->num_arr[$key], $sku_info['platform_price']]);
                        }
                    }
                } else
                    array_push($price_arr, $this->product_from_local['amount'] * $this->num);

                if (empty($price_arr))
                    throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);
                $data_arr['amount'] = sprintf('%.2f', array_sum(array_values($price_arr)))/* + $fee*/;
                $data_arr['fee'] = $fee;
                if ($data_arr['amount'] == 0)
                    throw new BookingException(BookingException::INVALID_PRICE_ARR, BookingException::INVALID_PRICE_ARR_NO);
                $data_arr['num'] = implode(',', $this->num_arr);
                $update_data = true;
            }

            // 修改收货地址 或修改商品数量 都需要重新计算运费
            if ($address_id != $data_arr['address_id'] || $update_data === true) {
                // 校验收货地址
                $address = Address::getAddressList([
                    'id'        => $address_id,
                    'from_api'  => true
                ]);
                $shipping = AlibabaProduct::getShippingFeeByProductIdProvince(
                    $this->product_id, $address['data'][0]['province'], $this->num);
                $fee = !empty($shipping) && isset($shipping['real_fee']) ? $shipping['real_fee'] : 0.00;
                // 重新计算运费
                /*$data_arr['amount'] = $data_arr['amount'] - $data_arr['fee']*//* + $fee*/;
                $data_arr['fee'] = $fee;
                $data_arr['address_id'] = $address_id;
                $update_data = true;
            }

            if ($update_data === true) {
                // 商品数量发生变化，删除掉旧的booking数据
                $this->redis->zRem($this->local_unpaid_coupon_member_list_key, $this->booking_data);
                // 把运费加回去
                $data_arr['amount'] = $data_arr['amount'] + $data_arr['fee'];
                // 写入新的booking数据
                $this->redis->zAdd($this->local_unpaid_coupon_member_list_key, [], $this->member_id, json_encode($data_arr));
                $data_arr['amount'] = $data_arr['amount'] - $data_arr['fee'];
            }

            $booking_data['address_id'] = $address_id;
            $booking_data['channel'] = $channel;
            $booking_data['num'] = $data_arr['num'];
            $booking_data['member_id'] = $this->member_id;
            // 把运费加回去
            $booking_data['amount'] = $data_arr['amount'] + $fee;
            $booking_data['fee'] = $fee;

            return $booking_data;
        } catch (BookingException $e) {
            Registry::get('order_log')->err("获取订购信息时报错，订购信息订单号: " . $sn . ", 商品编号: " . $this->product_id . ", sku: " . $this->sku_id . ", 原因: " . $e->getMessage());
            $this->closeRedis();
            throw $e;
        }
    }

    /**
     * 数据库事务
     */
    public function updateDb() {
        $this->db->beginTransaction();
        try {
            $this->db->commit();
        } catch (\Exception $e) {
            Registry::get('order_log')->err("数据库commit报错，原因: " . $e->getMessage());
            $this->db->rollback();
            throw $e;
        }
    }

    /**
     * 关闭redis
     */
    public function closeRedis() {
        if ($this->redis->isConnected())
            $this->redis->close();
    }

    /**
     * @param int $num
     */
    public function setNum($num)
    {
        $this->num = $num;
        array_push($this->num_arr, $num);
    }

    /**
     * @param string $sku_id
     */
    public function setSkuId($sku_id)
    {
        $this->sku_id = $sku_id;
        array_push($this->sku_arr, $sku_id);
        $this->local_product_sku_store_key = "product:store:" . $this->product_id . ':' . $sku_id;
    }

    /**
     * @return int
     */
    public function getNum()
    {
        return $this->num;
    }

    /**
     * @return string
     */
    public function getSkuId()
    {
        return $this->sku_id;
    }


}
